package com.spark.common.utils;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * 文件上传工具类（支持安全校验、自动目录创建、唯一文件名生成）
 *
 * @author LYCHEE
 * @date 2025/3/13 09:53
 */
public class FileUploadUtils {

    /**
     * 默认允许上传的扩展名（全小写）
     */
    public static final String[] DEFAULT_ALLOWED_EXTENSION = {
            // 图片
            "bmp", "gif", "jpg", "jpeg", "png",
            // word excel powerpoint
            "doc", "docx", "xls", "xlsx", "ppt", "pptx", "html", "htm", "txt",
            // 压缩文件
            "rar", "zip", "gz", "bz2",
            // 视频格式
            "mp4", "avi", "rmvb",
            // pdf
            "pdf" };


    /**
     * 核心上传方法（含安全校验）
     *
     * @param baseDir 相对应用的基目录
     * @param file    上传的文件
     * @return url
     */
    public static String upload(String baseDir, MultipartFile file) throws IOException {
        try{
            return upload(baseDir, file, DEFAULT_ALLOWED_EXTENSION);
        }catch (Exception e){
            throw new IOException(e.getMessage(), e);
        }
    }

    /**
     * 文件上传
     *
     * @param baseDir          相对应用的基目录
     * @param file             上传的文件
     * @param allowedExtension 上传文件类型
     * @return url
     */
    public static String upload(String baseDir, MultipartFile file, String[] allowedExtension) throws IOException {
        // 2. 后缀名统一小写处理
        String suffix = getFileSuffix(file).toLowerCase();
        checkFileType(suffix, allowedExtension);
        // 3. 生成安全文件名（过滤特殊字符）
        String fileName = codingFileName(file);
        // 4. 处理绝对路径并自动创建目录
        File destFile = getAbsoluteFile(baseDir, fileName);
        File parentDir = destFile.getParentFile();
        if (!parentDir.exists() && !parentDir.mkdirs()) {
            throw new IOException("目录创建失败: " + parentDir);
        }

        // 5. 防御性文件存在检查
        if (destFile.exists()) {
            throw new IOException("文件已存在: " + destFile.getName());
        }

        // 执行文件写入
        file.transferTo(destFile);
        return FilenameUtils.separatorsToUnix(fileName);
    }

    /**
     * 增强型文件类型校验（统一小写比较）
     * @param suffix 文件后缀
     * @param allowedExtension 文件类型
     * @throws IOException 是否可上传
     */
    public static void checkFileType(String suffix,String[] allowedExtension) throws IOException {
        Set<String> allowedSet = new HashSet<>(Arrays.asList(allowedExtension));
        if (!allowedSet.contains(suffix.toLowerCase())) {
            throw new IOException("不支持的文件类型: " + suffix);
        }
    }

    /**
     * 安全文件名生成（过滤路径符号）
     *
     * @param file 文件
     * @return 编码后的文件名称: {原始文件名称}_{文件ID}.{文件后缀}
     */
    public static String codingFileName(MultipartFile file) {
        // 防御路径遍历
        String originalName = FilenameUtils.getName(file.getOriginalFilename());
        // 特殊字符替换
        String baseName = FilenameUtils.getBaseName(originalName)
                .replaceAll("[^a-zA-Z0-9\\u4E00-\\u9FA5]", "_");
        return StringUtils.format("{}/{}_{}.{}",
                DateFormatUtils.format(new Date(),"yyyy/MM/dd"),
                baseName,
                RandomStringUtils.randomNumeric(13),
                getFileSuffix(file).toLowerCase()
        );
    }

    /**
     * 获取安全后缀（处理多扩展名如.tar.gz）
     *
     * @param file 文件
     * @return 后缀
     */
    public static String getFileSuffix(MultipartFile file) {
        String fullExtension = FilenameUtils.getExtension(file.getOriginalFilename());
        assert fullExtension != null;
        return fullExtension.contains(".") ?
                fullExtension.substring(fullExtension.lastIndexOf(".") + 1) :
                fullExtension;
    }

    /**
     * 绝对路径处理（跨平台路径分隔符支持）
     */
    private static File getAbsoluteFile(String baseDir, String fileName) {
        String normalizedBase = FilenameUtils.normalize(baseDir + File.separator);
        return new File(normalizedBase + fileName);
    }

    /**
     * 返回相对路径（适配不同存储方案）
     */
    public static String getPathFileName(String baseDir, String fileName) {
        return FilenameUtils.separatorsToUnix(
                Paths.get(baseDir, fileName).toString()
        );
    }

    /**
     * 安全删除临时文件（可选）
     */
    public static void safeDelete(File file) throws IOException {
        if (file != null && file.exists()) {
            Files.delete(file.toPath());
        }
    }


}
